package com.sig.util.id;

/**
 * 使用时间+机器ID+序列号方式生成唯一id FROM Twitter
 * 1位+41位时间+10位机器+12位序列
 *
 * @author sig
 * @since 2019/1/29
 */
public class GameSnowflake {
    /** 2016-05-01 00:00:00.0 开始使用这个生成方式的时候，不要超过上线时间 */
    public static final long usingEpoch = 1462032001000L;
    public static final int machineIdBits = 12;
    public static final int maxMachineId = -1 ^ (-1 << machineIdBits);
    public static final int sequenceBits = 12;
    public static final int timestampLeftShift = sequenceBits + machineIdBits;
    public static final int machineIdShift = sequenceBits;
    public static final int sequenceMask = -1 ^ (-1 << sequenceBits);

    private volatile int sequence = 0;
    private volatile long lastTimestamp;

    private final int machineId;

    public GameSnowflake(int machineId) {
        this.machineId = machineId;
        if (machineId < 0 || machineId > maxMachineId)
            throw new IllegalArgumentException(String.format("Machine id can't be greater than %d or less than 0,arg:" + machineId, maxMachineId));
    }

    public synchronized long generateId() {
        long now = System.currentTimeMillis();
        if (lastTimestamp == now) {
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) // had been get 4096 times at this ms...
                now = this.loopUtilNextMs(lastTimestamp);
        } else {
            sequence = 0; // reset seq every ms
        }
        lastTimestamp = now;
        return ((now - usingEpoch) << timestampLeftShift) | (machineId << machineIdShift) | sequence;
    }

    private long loopUtilNextMs(long timestamp) {
        long now = System.currentTimeMillis();
        while (now == timestamp)
            now = System.currentTimeMillis();
        return now;
    }
}
